package com.fsm.common.log.config;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson2.JSONObject;
import com.fsm.common.log.entity.TSystemLoginLog;
import com.fsm.common.log.entity.TSystemOperationLog;
import com.fsm.common.log.entity.table.TSystemLoginLogTable;
import com.fsm.common.log.mapper.TSystemLoginLogMapper;
import com.fsm.common.log.mapper.TSystemOperationLogMapper;
import com.fsm.common.tools.Result.Result;
import com.fsm.common.tools.Result.ResultCodeEnum;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.mybatisflex.core.query.QueryWrapper;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import java.lang.reflect.Method;
import java.util.*;

//日志记录切面管理
@Aspect//标记切面
@Component
@Log4j2
public class SysLogAspect {

    @Autowired
    private TSystemOperationLogMapper oTSystemOperationLogMapper;

    @Autowired
    private TSystemLoginLogMapper oTSystemLoginLogMapper;

    //定义切点 @Pointcut
    //在注解的位置切入代码
    @Pointcut("@annotation(com.fsm.common.log.config.LogData)")
    public void logPointCut() {
    }

    /**
     * 切面 配置通知
     *
     * @param joinPoint
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "keys")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveSysLog(JoinPoint joinPoint, Object keys) throws Exception {
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        //获取注解信息
        LogData logData = method.getAnnotation(LogData.class);
        //获取方法返回结果
        Result resultEnum = (Result)keys;
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        //判断是否是登录
        if(!logData.operation().toString().equals("EXIT")){
            if(logData.operation().toString().equals("LOGIN")){
                Integer logResult = loginLog(logData, joinPoint, resultEnum);
                //插入日志失败，数据库系统回滚
                if (logResult != 1) {
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                }
            }else{
                Integer logResult = operationLog(request, logData, joinPoint, resultEnum);
                //插入日志失败，数据库系统回滚
                if (logResult != 1) {
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                }
            }
        }
    }

    /**
     * 登录日志记录
     *
     * @param request 请求信息
     * @param logData 日志注解
     * @return 日志记录结果
     */
    private int loginLog(LogData logData, JoinPoint joinPoint, Result resultEnum) throws Exception {
        //获取方法返回结果
        if(resultEnum != null){
            int sign = resultEnum.getCode();
            if(sign != ResultCodeEnum.SUCCESS.getCode()){
                //登录失败不记录
                return 1;
            }
        }
        Object[] args = joinPoint.getArgs();
        List<Object> objectList = new ArrayList<>(args.length);
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                continue;
            }
            objectList.add(args[i]);
        }
        //插入数据
        TSystemLoginLog loginLog = new TSystemLoginLog();
        loginLog.setSystemType(logData.systemTypeEnum().getMessage());
        //获取用户(此处判断是哪个系统,取哪个用户值)
        String userId = StpUtil.getLoginIdAsString();
        if(StringUtils.isBlank(userId)){
            throw new Exception("登录用户不存在");
        }
        loginLog.setUserId(userId);
        return oTSystemLoginLogMapper.insert(loginLog);
    }

    /**
     * 菜单日志记录
     *
     * @param request 请求信息
     * @param logData 日志注解
     * @return 日志记录结果
     */
    private int operationLog(HttpServletRequest request, LogData logData, JoinPoint joinPoint, Result resultEnum) throws Exception {
        Object[] args = joinPoint.getArgs();
        List<Object> objectList = new ArrayList<>(args.length);
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ServletRequest || args[i] instanceof ServletResponse || args[i] instanceof MultipartFile) {
                continue;
            }
            objectList.add(args[i]);
        }
        //取出传入的数据
        StringBuffer stringBuffer = new StringBuffer();
        if(objectList.size() != 0 && objectList != null){
            for(Object objectData : objectList){
                //获取该对象的所有字段
                java.lang.reflect.Field[] fields = objectData.getClass().getDeclaredFields();
                HashMap<String,String> fieldNameMap = new HashMap<>();
                for (java.lang.reflect.Field field : fields) {
                    //获取数据库映射的Id
                    if (field.isAnnotationPresent(Schema.class)) {
                        Schema tableField = field.getAnnotation(Schema.class);
                        //处理有数据库映射的字段且dataMap中含有该key值
                        //如果与数据库相同，则进行赋值
                        fieldNameMap.put(tableField.name(),tableField.description());
                    }
                }
                String recordKey = JSONObject.toJSONString(objectData);
                if(recordKey.contains("{") && recordKey.contains("}") && recordKey.contains(":")){
                    try{
                        Map<String,Object> map = JSONObject.parseObject(recordKey,Map.class);
                        for(String strKey : map.keySet()){
//                            stringBuffer.append(fieldNameMap.get(strKey) + "=" + map.get(strKey) + ";");
                            stringBuffer.append(strKey + "=" + map.get(strKey) + ";");
                        }
                    }catch (Exception e){
                        stringBuffer.append(recordKey+";");
                    }
                }else{
                    stringBuffer.append(recordKey+";");
                }
            }
        }
        //插入数据
        TSystemOperationLog operationLog = new TSystemOperationLog();
        operationLog.setSystemType(logData.systemTypeEnum().getMessage());
        //获取用户
        String userId = StpUtil.getLoginIdAsString();
        if(StringUtils.isBlank(userId)){
            throw new Exception("登录用户不存在");
        }
        operationLog.setUserId(userId);
        operationLog.setMenu(request.getRequestURI());
        operationLog.setOperationType(logData.operation().getMessage());
        operationLog.setRecordKey(stringBuffer.toString());
        operationLog.setComments(logData.value());
        //获取方法返回结果
        int sign = resultEnum.getCode();
        if(sign == ResultCodeEnum.SUCCESS.getCode()){
            operationLog.setCallState(1);
            if(resultEnum.getData() != null || CollectionUtils.isNotEmpty(resultEnum.getDataList())){
                if(resultEnum.getData() != null){
                    operationLog.setReturnKey(resultEnum.getData().toString());
                }else{
                    operationLog.setReturnKey(resultEnum.getDataList().toString());
                }
            }
        }else{
            operationLog.setCallState(0);
        }
        return oTSystemOperationLogMapper.insert(operationLog);
    }

    /**
     * 切面 配置通知 退出
     *
     * @param joinPoint
     */
    @Before("logPointCut()")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveSysExitLog(JoinPoint joinPoint) throws Exception {
        //从切面织入点处通过反射机制获取织入点处的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点所在的方法
        Method method = signature.getMethod();
        //获取注解信息
        LogData logData = method.getAnnotation(LogData.class);
        //判断是否是登录
        if(logData.operation().toString().equals("EXIT")){
            //获取用户
            String userId = StpUtil.getLoginIdAsString();
            if(StringUtils.isBlank(userId)){
                throw new Exception("登录用户不存在");
            }
            //修改数据
            QueryWrapper queryWrapper = new QueryWrapper();
            queryWrapper.where(TSystemLoginLogTable.T_SYSTEM_LOGIN_LOG.USER_ID.eq(userId));
            queryWrapper.where(TSystemLoginLogTable.T_SYSTEM_LOGIN_LOG.SYSTEM_TYPE.eq(logData.systemTypeEnum().getMessage()));
            queryWrapper.orderBy("login_time desc");
            queryWrapper.limit(1);
            TSystemLoginLog loginLog = oTSystemLoginLogMapper.selectOneByQuery(queryWrapper);
            if(loginLog != null){
                loginLog.setExitTime(new Date());
                if(oTSystemLoginLogMapper.update(loginLog) == 0){
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                }
            }
        }
    }

}

